/**
 * 
 * @Project HRL Simulation
 * @Date 20.06.2011
 * @Author Aebi Pascal, Kocher Marc, Sriranjan George
 * @School BFH-TI Biel, Projekt 1
 *
 */

package gui;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;

import exceptions.LoadListException;

import gui.views.HRLMainFrame;
import gui.views.SettingsCardPanel;
import gui.views.StatisticsFrame;
import gui.views.ViewerCardPanel;


public class ModelViewController {

	private HRLMainFrame jfHRL;
	private StatisticsFrame jfStatistics;
	private Model model;
	private SettingsCardPanel scpvSettings;
	private ViewerCardPanel vcpvViewer;
	private boolean finished;
	private int progress = 0;
	
	
	/**
	 * Creates a new HRL model controller
	 * 
	 * @param model The HRL GUI model
	 * @param jfHRL The HRL main frame
	 * @param jfStatistics The statistics frame
	 * @param scpvSettings The settings panel
	 * @param vcpvViewer The viewer panel
	 */
	public ModelViewController(Model model, HRLMainFrame jfHRL, StatisticsFrame jfStatistics, SettingsCardPanel scpvSettings, ViewerCardPanel vcpvViewer){
		this.model = model;
		this.jfHRL = jfHRL;
		this.jfStatistics = jfStatistics;
		this.scpvSettings = scpvSettings;
		this.vcpvViewer = vcpvViewer;
		
		SettingsCardListener sclSettings = new SettingsCardListener();
		ViewerCardListener vclView = new ViewerCardListener();
		HRLListener hrllHRL = new HRLListener();
		
		this.jfHRL.addHRLActionListener(hrllHRL);
		
		this.scpvSettings.addSettingsActionListener(sclSettings);
		this.scpvSettings.addSettingsChangeListener(sclSettings);
		this.scpvSettings.addSettingsItemListener(sclSettings);
		
		this.vcpvViewer.addViewerActionListener(vclView);
	}
	
	
	/**
	 * The listener class of the HRL main frame
	 */
	class HRLListener implements ActionListener {
			@Override
			public void actionPerformed(ActionEvent event) {
				// Menu item start new simulation
				if(event.getSource().equals(jfHRL.getJmiNew())){
					// Set settings panel active
					jfHRL.setActiveCardPanel(model.getStringSettings());
					// Disable menu item start new simulation
					jfHRL.getJmiNew().setEnabled(false);
					// Enable menu item save
					jfHRL.getJmiSave().setEnabled(true);
				}
				
				// Menu item load properties
				if(event.getSource().equals(jfHRL.getJmiLoad())){
					// Load properties from file
					model.loadProperties(jfHRL);
					
					if(model.getSelectedFile() != null){
						// Set property values in gui
						scpvSettings.setPropertyValues();
						// Disable menu item new simulation
						jfHRL.getJmiNew().setEnabled(false);
						// Enable menu item save
						jfHRL.getJmiSave().setEnabled(true);
						// Set settings panel active
						jfHRL.setActiveCardPanel(model.getStringSettings());
					}
				}
				
				// Menu item save properties
				if(event.getSource().equals(jfHRL.getJmiSave())){
					// Get actual property values from gui
					scpvSettings.getPropertyValues();
					// Save properties in file
					model.saveProperties(jfHRL);
				}
				
				// Menu item exit (exit program)
				if(event.getSource().equals(jfHRL.getJmiExit())){
					System.exit(0);
				}
				
				// Menu item settings (show settings panel)
				if(event.getSource().equals(jfHRL.getJmiSettings())){
					// Set settings panel active
					jfHRL.setActiveCardPanel(model.getStringSettings());
					// Enable menu item save
					jfHRL.getJmiSave().setEnabled(true);
					// Disable menu item load
					jfHRL.getJmiLoad().setEnabled(true);
				}
				
				// Menu item viewer (show viewer panel)
				if(event.getSource().equals(jfHRL.getJmiViewer())){
					// Set viewer panel active
					jfHRL.setActiveCardPanel(model.getStringViewer());
					// Disable menu item save
					jfHRL.getJmiSave().setEnabled(false);
					// Disable menu item load
					jfHRL.getJmiLoad().setEnabled(false);
				}
				
				// Menu item version
				if(event.getSource().equals(jfHRL.getJmiVersion())){	 
					JLabel labelVersion = new JLabel(model.getStringVersionInfo());
					JOptionPane.showConfirmDialog(null, labelVersion, "HRL-Simulation Version", JOptionPane.CLOSED_OPTION, JOptionPane.INFORMATION_MESSAGE);
				} 
				
				// Menu item about
				if(event.getSource().equals(jfHRL.getJmiAbout())){
					JPanel jpAuthors = new JPanel();
					jpAuthors.setPreferredSize(new Dimension(220, 140));
					for(int i=0; i<model.getAuthors().length; i++){
						JLabel jlAuthor = new JLabel(model.getAuthors()[i]);
						jlAuthor.setPreferredSize(new Dimension(220, 40));
						jpAuthors.add(jlAuthor);
					}
					JOptionPane.showConfirmDialog(null, jpAuthors, "HRL-Simulation Authors", JOptionPane.CLOSED_OPTION, JOptionPane.PLAIN_MESSAGE);
				}
				
				// Menu item instructions
				if(event.getSource().equals(jfHRL.getJmiInstructions())){  	
					JPanel pNavi = new JPanel();
					
					// arrow keys
					String sArrow = "With the arrow keys you are able to navigate to forward, " +
							"<br>backward, left and right. This means, you move just the " +
							"<br>view and not the positon.";
					JLabel lArrow = new JLabel("<html><u>Navigation with arrow keys</u><br>"+sArrow+"</html>");
			
					// mouse keys
					String sMouse1 = "1: &nbsp;&nbsp; With the left mouse button,  you can rotate the canvas.";
					String sMouse2 = "2: &nbsp;&nbsp; With the right mouse button, you can shift the canvas.";
					String sMouse3 = "3: &nbsp;&nbsp; With the scroll wheel, you can zoom in and out. " +
							"<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If your mouse don't have a scroll wheel, don't worry," +
							"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; you can press the ALT key and use the left mouse button (1) instead.";
					JLabel lMouse = new JLabel("<html><u>Navigation with mouse buttons</u><br>"+sMouse1+"<br><br>"+sMouse2+"<br><br>"+sMouse3+"</html>");
					
					// buttons
					// reset
					String sReset = "Resets the simulation to the start time";
					JLabel lReset = new JLabel("<html><u>Reset button</u><br>"+sReset+"</html>");
					
					// backwards
					String sBack = "Each click slows the simulation speed.";
					JLabel lBack = new JLabel("<html><u>Backward button</u><br>"+sBack+"</html>");
					// 	start		
					String sPlay = "The simulation starts. Press Again for Pause and vice versa.";
					JLabel lPlay = new JLabel("<html><u>Play button</u><br>"+sPlay+"</html>");
					
					// Forward
					String sForward = "Each click raises the simulation speed";
					JLabel lForward = new JLabel("<html><u>Forward button</u><br>"+sForward+"</html>");
					// statistics
					String sStatistics = "Opens a new window with the statistics.";
					JLabel lStatistics = new JLabel("<html><u>Statistics button</u><br>"+sStatistics+"</html>");
					
					//pNavi.setSize(300, 300);			
					BoxLayout box = new BoxLayout(pNavi, BoxLayout.Y_AXIS);
					pNavi.setLayout(box);
					
					pNavi.add(lArrow);
					pNavi.add(Box.createRigidArea(new Dimension(0,30)));
					pNavi.add(lMouse);
					pNavi.add(Box.createRigidArea(new Dimension(0,30)));
					pNavi.add(new JSeparator());
					pNavi.add(Box.createRigidArea(new Dimension(0,30)));
					pNavi.add(lReset);
					pNavi.add(Box.createRigidArea(new Dimension(0,30)));
					pNavi.add(lBack);
					pNavi.add(Box.createRigidArea(new Dimension(0,30)));
					pNavi.add(lPlay);
					pNavi.add(Box.createRigidArea(new Dimension(0,30)));
					pNavi.add(lForward);
					pNavi.add(Box.createRigidArea(new Dimension(0,30)));
					pNavi.add(lStatistics);
					
					JOptionPane.showConfirmDialog(pNavi, pNavi, "How to navigate...", JOptionPane.CLOSED_OPTION, JOptionPane.PLAIN_MESSAGE);
				}
			}
		}


	/**
	 * The listener class of the settings panel
	 */
	class SettingsCardListener implements ActionListener, ChangeListener, ItemListener {
		@Override
		public void itemStateChanged(ItemEvent event) {
			// Random strategy
			if(event.getItem().equals(model.getStrategies()[0][0])){
				scpvSettings.getJtaStrategyDescription().setText(model.getStrategies()[0][1]);
			}
			
			// Successive left right strategy
			if(event.getItem().equals(model.getStrategies()[1][0])){
				scpvSettings.getJtaStrategyDescription().setText(model.getStrategies()[1][1]);
			}
			
			// Successive top down strategy
			if(event.getItem().equals(model.getStrategies()[2][0])){
				scpvSettings.getJtaStrategyDescription().setText(model.getStrategies()[2][1]);
			}
			
			// Optimized strategy
			if(event.getItem().equals(model.getStrategies()[3][0])){
				scpvSettings.getJtaStrategyDescription().setText(model.getStrategies()[3][1]);
			}
			
			// In check box
			if(event.getItem().equals(scpvSettings.getJcbIn())){
				Integer iRandomLoadNumber = Integer.parseInt(scpvSettings.getJspRandomLoadNumber().getValue().toString());
				if(iRandomLoadNumber > 1 && (scpvSettings.getJcbIn().isSelected() || scpvSettings.getJcbStore().isSelected())){
					scpvSettings.getJcbOut().setEnabled(true);
				}
				else{
					scpvSettings.getJcbOut().setSelected(false);
					scpvSettings.getJcbOut().setEnabled(false);
				}
			}
			
			// Store check box
			if(event.getItem().equals(scpvSettings.getJcbStore())){
				Integer iRandomLoadNumber = Integer.parseInt(scpvSettings.getJspRandomLoadNumber().getValue().toString());
				if(iRandomLoadNumber > 1 && (scpvSettings.getJcbIn().isSelected() || scpvSettings.getJcbStore().isSelected())){
					scpvSettings.getJcbOut().setEnabled(true);
				}
				else{
					scpvSettings.getJcbOut().setSelected(false);
					scpvSettings.getJcbOut().setEnabled(false);
				}
			}
		}
		
		
		@Override
		public void stateChanged(ChangeEvent event) {
			// Spinner lift number 
			if(event.getSource().equals(scpvSettings.getJspLiftNumber())){
				// Calculate rack number
				Integer rackNumber = model.calculateRackNumber((Integer) scpvSettings.getJspLiftNumber().getValue());
				scpvSettings.getJtfRackNumber().setText(rackNumber.toString());
				
				// Calculate maximum possible random loads
				Integer rackLength = (Integer) scpvSettings.getJspRackLength().getValue();
				Integer rackHeight = (Integer) scpvSettings.getJspRackHeight().getValue();
				Integer maxRandomLoads = model.calculateMaxRandomLoadNumber(rackNumber, rackLength, rackHeight);
				if(maxRandomLoads < (Integer) scpvSettings.getSnmRandomLoadNumber().getMaximum() && maxRandomLoads < (Integer) scpvSettings.getJspRandomLoadNumber().getValue()){
					scpvSettings.getJspRandomLoadNumber().setValue(maxRandomLoads);
				}
				scpvSettings.getSnmRandomLoadNumber().setMaximum(maxRandomLoads);
			}
			
			// Spinner rack length
			if(event.getSource().equals(scpvSettings.getJspRackLength())){
				// Calculate maximum possible random loads
				Integer rackNumber = model.calculateRackNumber((Integer) scpvSettings.getJspLiftNumber().getValue());
				Integer rackLength = (Integer) scpvSettings.getJspRackLength().getValue();
				Integer rackHeight = (Integer) scpvSettings.getJspRackHeight().getValue();
				Integer maxRandomLoads = model.calculateMaxRandomLoadNumber(rackNumber, rackLength, rackHeight);
				if(maxRandomLoads < (Integer) scpvSettings.getSnmRandomLoadNumber().getMaximum() && maxRandomLoads < (Integer) scpvSettings.getJspRandomLoadNumber().getValue()){
					scpvSettings.getJspRandomLoadNumber().setValue(maxRandomLoads);
				}
				scpvSettings.getSnmRandomLoadNumber().setMaximum(maxRandomLoads);
			}
			
			// Spinner rack height
			if(event.getSource().equals(scpvSettings.getJspRackHeight())){
				// Calculate maximum possible random loads
				Integer rackNumber = model.calculateRackNumber((Integer) scpvSettings.getJspLiftNumber().getValue());
				Integer rackLength = (Integer) scpvSettings.getJspRackLength().getValue();
				Integer rackHeight = (Integer) scpvSettings.getJspRackHeight().getValue();
				Integer maxRandomLoads = model.calculateMaxRandomLoadNumber(rackNumber, rackLength, rackHeight);
				if(maxRandomLoads < (Integer) scpvSettings.getSnmRandomLoadNumber().getMaximum() && maxRandomLoads < (Integer) scpvSettings.getJspRandomLoadNumber().getValue()){
					scpvSettings.getJspRandomLoadNumber().setValue(maxRandomLoads);
				}
				scpvSettings.getSnmRandomLoadNumber().setMaximum(maxRandomLoads);
			}
			
			// Spinner random load number
			if(event.getSource().equals(scpvSettings.getJspRandomLoadNumber())){
				Integer iLoadNumber = Integer.parseInt(scpvSettings.getJspRandomLoadNumber().getValue().toString());
				JCheckBox jbcOut = scpvSettings.getJcbOut();
				if(iLoadNumber != 1 && (scpvSettings.getJcbIn().isSelected() || scpvSettings.getJcbStore().isSelected())){
					jbcOut.setEnabled(true);
				}
				else{
					jbcOut.setSelected(false);
					jbcOut.setEnabled(false);
				}
			}
			
			// Slider conveyor speed
			if(event.getSource().equals(scpvSettings.getJslConveyorSpeed())){
				Integer iConveyorSpeed = scpvSettings.getJslConveyorSpeed().getValue();
				scpvSettings.getJtfConveyorSpeed().setText(iConveyorSpeed.toString());
				
			}
			
			// Slider lift speed
			if(event.getSource().equals(scpvSettings.getJslLiftSpeed())){
				Integer iLiftSpeed = scpvSettings.getJslLiftSpeed().getValue();
				scpvSettings.getJtfLiftSpeed().setText(iLiftSpeed.toString());
				
			}
			
			// Slider simulation speed
			if(event.getSource().equals(scpvSettings.getJslSimulationSpeed())){
				int sliderValue = scpvSettings.getJslSimulationSpeed().getValue();
				Integer iSimulationSpeed = model.convertSimlationSpeed(sliderValue);
				
				if(sliderValue < 5){
					scpvSettings.getJtfSimulationSpeed().setText(iSimulationSpeed.toString());
					scpvSettings.getJlSimulationSpeedUnitText().setText("times slower");
				}
				else if(sliderValue > 5){
					scpvSettings.getJtfSimulationSpeed().setText(iSimulationSpeed.toString());
					scpvSettings.getJlSimulationSpeedUnitText().setText("times faster");
				}
				else{
					scpvSettings.getJtfSimulationSpeed().setText("");
					scpvSettings.getJlSimulationSpeedUnitText().setText("normal");
				}
			}
		}
		
		
		@Override
		public void actionPerformed(ActionEvent event) {
			// Button set maximum random loads
			if(event.getSource().equals(scpvSettings.getJbMaxRandomLoads())){
				Integer rackNumber = model.calculateRackNumber((Integer) scpvSettings.getJspLiftNumber().getValue());
				Integer rackLength = (Integer) scpvSettings.getJspRackLength().getValue();
				Integer rackHeight = (Integer) scpvSettings.getJspRackHeight().getValue();
				Integer maxRandomLoads = model.calculateMaxRandomLoadNumber(rackNumber, rackLength, rackHeight);
				scpvSettings.getJspRandomLoadNumber().setValue(maxRandomLoads);
			}
			
			// Button add manual load
			if(event.getSource().equals(scpvSettings.getJbAddLoad())){
				String[] row = {scpvSettings.getJtfLoadName().getText(),
								scpvSettings.getJtfClassOfGood().getText(),
								scpvSettings.getJcbLoadType().getSelectedItem().toString(),
								scpvSettings.getJtfArrivalTime().getText()
							   };
				model.addManualLoad(row);
				scpvSettings.getDtmManualLoads().addRow(row);
				scpvSettings.getJtfLoadName().setText("");
				scpvSettings.getJtfClassOfGood().setText("");
				scpvSettings.getJcbLoadType().setSelectedIndex(0);
				scpvSettings.getJtfArrivalTime().setText("");
			}
			
			// Button delete selected table entries
			if(event.getSource().equals(scpvSettings.getJbDeleteSelectedEntries())){
				JTable jtManualLoads = scpvSettings.getJtManualLoads();
				int[] aSelectedRows = jtManualLoads.getSelectedRows();
				for(int i=aSelectedRows.length-1; i>=0; i--){
					((DefaultTableModel) jtManualLoads.getModel()).removeRow(aSelectedRows[i]);
					model.removeManualLoad(aSelectedRows[i]);
				}
			}
			
			// Button delete all table entries
			if(event.getSource().equals(scpvSettings.getJbDeleteAllEntries())){
				JTable jtManualLoads = scpvSettings.getJtManualLoads();
				model.clearManualLoadsTable();
			    while (jtManualLoads.getRowCount() > 0) {
			        ((DefaultTableModel) jtManualLoads.getModel()).removeRow(0);
			    }
			}
			
			// Button apply settings
			if(event.getSource().equals(scpvSettings.getJbApplySettings())){
				// Get actual property values from gui
				scpvSettings.getPropertyValues();
				
				// Create control center
				model.createControlCenter();
				// Set strategy
				model.setStrategy();
				
				// Generate loads
				try {
					model.generateLoads();
				} catch (LoadListException e) {
					// TODO handle this conflict
					e.printStackTrace();
				}
				
				// Schedule loads
				try {
					model.scheduleLoads();
				} catch (Exception e) {
					e.printStackTrace();
					JOptionPane.showMessageDialog(scpvSettings, e.getMessage(),"Error",JOptionPane.ERROR_MESSAGE);
				}
				
				// Create statistics manager
				model.createStatisticsManager(jfStatistics);
				
				// Clear all components from simulation before
				if(vcpvViewer.getJpSimulationPanel().getComponentCount() > 0){
					vcpvViewer.getJpSimulationPanel().removeAll();
				}
				// Create 3D world
				model.createWorld3D();
				// Add world 3D to simulation panel
				vcpvViewer.getJpSimulationPanel().add(model.getWorld3D());
				
				// Reset previous simulation animation
				model.getWorld3D().resetAnimation();
				
				// Disable reset button
				vcpvViewer.getJbReset().setEnabled(false);
				// Enable play button
				vcpvViewer.getJbPlay().setEnabled(true);
				// Set text of play/pause button to play
				vcpvViewer.getJbPlay().setText("Play");
				
				progress = 0;
				finished = true;
				vcpvViewer.getJpbSimulationProgress().setValue(progress);
				
				// Set viewer panel active
				jfHRL.setActiveCardPanel("Viewer");
				// Enable menu view
				jfHRL.getJmView().setEnabled(true);
				// Disable menu item save 
				jfHRL.getJmiSave().setEnabled(false);
				// Disable menu item load
				jfHRL.getJmiLoad().setEnabled(false);
			}
			
			// Radio button generate loads randomly
			if(event.getSource().equals(scpvSettings.getJrbRandomLoads())){
				scpvSettings.enableRandomLoadPanel();
			}
			
			// Radio button generate loads manually
			if(event.getSource().equals(scpvSettings.getJrbManualLoads())){
				scpvSettings.enableManualLoadPanel();
			}
		}
	}
	
	
	/**
	 * The listener class of the viewer panel
	 */
	class ViewerCardListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent event) {
			// Button reset
			if(event.getSource().equals(vcpvViewer.getJbReset())){
				vcpvViewer.getJbReset().setEnabled(false);
				vcpvViewer.getJbPlay().setEnabled(true);
				vcpvViewer.getJbPlay().setText("Play");
				model.getWorld3D().resetAnimation();
				progress = 0;
				finished = true;
				vcpvViewer.getJpbSimulationProgress().setValue(progress);
			}
			
			// Button slow down
			if(event.getSource().equals(vcpvViewer.getJbSlowDown())){
				model.getWorld3D().slowDownAnimation();
			}
			
			// Button play/pause
			if(event.getSource().equals(vcpvViewer.getJbPlay())){
				if(vcpvViewer.getJbPlay().getText().equals("Play")){
					vcpvViewer.getJbPlay().setText("Pause");
					vcpvViewer.getJbReset().setEnabled(true);
					model.getWorld3D().startAnimation();
					if(progress <=100){
						finished = false;
						new Thread(
							new Runnable() {
								public void run() {
									while (!finished) {
										try {
							                  Thread.sleep((long)( model.getWorld3D().getDuration()/100));
							                } catch (InterruptedException e) {}
										if(progress == 100){
											finished = true;
											// Disable play button
											vcpvViewer.getJbPlay().setEnabled(false);
										}
										vcpvViewer.getJpbSimulationProgress().setValue(progress);
										progress++;
									}
								}
							}
						).start();
					}
				}
				else{
					vcpvViewer.getJbPlay().setText("Play");
					model.getWorld3D().pauseAnimation();
					finished = true;
					vcpvViewer.getJpbSimulationProgress().setValue(0);
				}
			}
			
			// Button speed up
			if(event.getSource().equals(vcpvViewer.getJbSpeedUp())){
				model.getWorld3D().speedUpAnimation();
			}
			
			// Button statistics
			if(event.getSource().equals(vcpvViewer.getJbStatistics())){
				jfStatistics.setVisible(true);
			}
		}
	}
	
}
